package com.filepd.service.impl;

import cn.hutool.json.JSONObject;
import cn.novelweb.tool.upload.local.LocalUpload;
import cn.novelweb.tool.upload.local.pojo.UploadFileParam;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.filepd.common.R;
import com.filepd.config.FtpProperties;
import com.filepd.entity.File;
import com.filepd.mapper.FileMapper;
import com.filepd.service.FileService;
import com.filepd.utils.WeBASEUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
* @author Administrator
* @description 针对表【file】的数据库操作Service实现
* @createDate 2023-12-20 08:26:58
*/
@Service
@Slf4j
public class FileServiceImpl extends ServiceImpl<FileMapper, File>
    implements FileService{

    @Autowired
    private WeBASEUtils weBASEUtils;

    @Autowired
    private FtpProperties ftpProperties;

    @Autowired
    private FileMapper fileMapper;

    @Value("${file.save-path:/data-center/files/vip-file-manager}")
    private String savePath;
    @Value("${file.conf-path:/data-center/files/vip-file-manager/conf}")
    private String confFilePath;

    private FTPClient connectFtpServer() {
        // 创建FTPClient对象（对于连接ftp服务器，以及上传和上传都必须要用到一个对象）
        log.info("创建FTP连接");
        FTPClient ftpClient = new FTPClient();
        // 设置连接超时时间
        ftpClient.setConnectTimeout(1000 * 30);
        // 设置ftp字符集
        ftpClient.setControlEncoding("utf-8");
        try {
            // 定义返回的状态码
            int replyCode;
            // 连接ftp(当前项目所部署的服务器和ftp服务器之间可以相互通讯，表示连接成功)
            ftpClient.connect(ftpProperties.getHost(), ftpProperties.getPort());
            // 输入账号和密码进行登录
            ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            // 设置被动模式，文件传输端口设置,否则文件上传不成功，也不报错
            ftpClient.enterLocalPassiveMode();
            // 接受状态码(如果成功，返回230，如果失败返回503)
            replyCode = ftpClient.getReplyCode();
            // 根据状态码检测ftp的连接，调用isPositiveCompletion(reply)-->如果连接成功返回true，否则返回false
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                log.info("连接 ftp {} 失败", ftpProperties.getHost());
                // 连接失败，断开连接
                ftpClient.disconnect();
                return null;
            }
            log.info("replyCode:" + replyCode);
        } catch (IOException e) {
            log.error("连接失败:" + e.toString());
            return null;
        }
        return ftpClient;
    }

    /**
     * 文件上传
     * @param file
     * @return
     */
    // TODO 只能上传小文件 大文件切割上传/断点续传
    @Override
    public R<String> uploadFile(String userName, MultipartFile file) {
        try {
            // 连接到FTP服务器
            FTPClient ftpClient = connectFtpServer();

            // 获取文件名和输入流
            String fileName = file.getOriginalFilename();

            // 获取文件大小
            long fileSize = file.getSize();
            String Size = "";
            if (fileSize < 1024) {
                Size = fileSize + " bytes";
            } else if (fileSize < 1024 * 1024) {
                long kilobytes = fileSize / 1024;
                Size = kilobytes + " KB";
            } else {
                long megabytes = fileSize / (1024 * 1024);
                Size = megabytes + " MB";
            }
            InputStream inputStream = file.getInputStream();

            //获取文件类型
            int lastIndex  = fileName.lastIndexOf(".");
            String contentType="";
            if (lastIndex != -1) {
                contentType = fileName.substring(lastIndex, fileName.length());
                System.out.println(contentType);
            } else {
                System.out.println("无法找到指定字符'.");
            }

            // 设置文件类型为二进制
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            log.info("文件名 " + fileName);

            // 创建目录并赋予777权限
            String directory = "/path";
            ftpClient.makeDirectory(directory);
            ftpClient.sendCommand("SITE CHMOD 777 " + directory);

            // 切换到指定目录
            ftpClient.changeWorkingDirectory(directory);

            // 设置缓冲区
            ftpClient.setBufferSize(1024 * 1024 * 100);

            // 上传文件到FTP服务器
            ftpClient.storeFile(fileName, inputStream);

            // 数据上链
            if (StringUtils.isEmpty(userName)) {
                userName = "未知";
            }
            JSONObject set = weBASEUtils.set(userName, fileName, Size, contentType);
            String result = set.getStr("result");
            JSONObject entries = new JSONObject(result);
            String blockHash = entries.getStr("blockHash");
            // 获取blockHash,保存数据库中
            File file1 = new File();
            file1.setUserName(userName);
            file1.setFileName(fileName);
            file1.setFileSize(Size);
            file1.setFileType(contentType);
            file1.setFileHash(blockHash);
            fileMapper.insert(file1);


            // 关闭FTP连接和输入流
            inputStream.close();
            ftpClient.logout();
            ftpClient.disconnect();

            log.info("上传成功");
            return R.success("文件上传成功");

        } catch (IOException e) {
            e.printStackTrace();
            log.info("上传失败");
            return R.error("文件上传失败");
        }
    }

    /**
     * 文件下载
     *
     * @param fileName
     * @param ftpFilePath
     * @param response
     */
    public R<String> downloadFileToFtp(String fileName, String ftpFilePath, HttpServletResponse response) {
        FTPClient ftpClient = connectFtpServer();
        try {
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.enterLocalPassiveMode();
            // 设置文件ContentType类型，这样设置，会自动判断下载文件类型
            response.setContentType("application/x-msdownload");
            // 设置文件头：最后一个参数是设置下载的文件名并编码为UTF-8
            response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
            InputStream is = ftpClient.retrieveFileStream(ftpFilePath);
            BufferedInputStream bis = new BufferedInputStream(is);
            OutputStream out = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = bis.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            out.flush();
            out.close();

            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            ftpClient.logout();
        } catch (Exception e) {
            log.error("FTP文件下载失败:", e);
            return R.error("文件下载失败");
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException ioe) {
                    log.error(ioe.toString());
                    return R.error("文件下载失败");
                }
            }
        }
        return R.error("文件下载成功");
    }

    /**
     * 得到排序后的目录
     * @return
     */
    @Override
    public R<List<String>> getFilesFromFtpDirectory() {

        List<String> fileList = new ArrayList<>();

        // 连接到FTP服务器
        FTPClient ftpClient = connectFtpServer();

        try {
            // 切换到对应的目录
            ftpClient.changeWorkingDirectory("/path");

            FTPFile[] files = ftpClient.listFiles();


            // 通过文件修改时间排序
            Arrays.sort(files, new Comparator<FTPFile>() {
                public int compare(FTPFile file1, FTPFile file2) {
                    return file2.getTimestamp().compareTo(file1.getTimestamp());
                }
            });

            // 添加到fileList
            for (FTPFile file : files) {
                if (file.isFile()) {
                    String fileName = file.getName();
                    fileList.add(fileName);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //添加动态length数据
        R result =  R.success(fileList);
        int size = fileList.size()-1;
        result.add("length", size);

        return result;
    }

    /**
     * 在数据库中查到所有数据
     *
     * @return
     */
    @Override
    public List<File> getBlockAll() {

        List<File> files = fileMapper.selectList(null);
        return files;
    }

    /**
     * 得到最新
     * @return
     */
    @Override
    public File getNewBlock() {
        LambdaQueryWrapper<File> fileLambdaQueryWrapper = new LambdaQueryWrapper<>();
        fileLambdaQueryWrapper.orderByDesc(File::getCreateTime).last("limit 1");
        return fileMapper.selectOne(fileLambdaQueryWrapper);
    }

    @Override
    public R<String> breakpointResumeUpload(UploadFileParam param, HttpServletRequest request) {
        try {
            // 这里的 chunkSize(分片大小) 要与前端传过来的大小一致
            cn.novelweb.tool.http.Result result = LocalUpload.fragmentFileUploader(param,5242880L, request);
            return R.success(String.valueOf(result));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return R.error("上传失败");
    }


    @Override
    public R<String> checkFileMd5(String md5, String fileName) {
        try {
            cn.novelweb.tool.http.Result result = LocalUpload.checkFileMd5(md5, fileName, confFilePath, savePath);
            return R.success(String.valueOf(result));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return R.error("上传失败");
    }

    @Override
    public Boolean sendContract(String userAccount, String userName, String fileName, String fileType, double fileSize) {
        return null;
    }
    /**
     * 旧的文件上传
     * 规范化, 摒弃
     * @param file
     * @return
     */
    // @PostMapping(consumes = "multipart/*", headers = "content-type=multipart/form-data")
    // public R<String> uploadFile(@RequestParam("file") MultipartFile file) {
    //     log.info("开始上传文件...");
    //     try {
    //         // 创建FTPClient对象
    //         FTPClient ftpClient = new FTPClient();
    //
    //         //设置编码,解决上传文件名乱码问题
    //         ftpClient.setControlEncoding("UTF-8");
    //         // 连接到FTP服务器
    //         ftpClient.connect(host, 21);
    //         ftpClient.login(username, password);
    //
    //         //设置被动连接
    //         ftpClient.enterLocalPassiveMode();
    //
    //         // 设置文件类型为二进制
    //         ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    //
    //         // 获取文件名和输入流
    //         String fileName = file.getOriginalFilename();
    //         InputStream inputStream = file.getInputStream();
    //
    //         // 设置文件类型为二进制
    //         ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    //         log.info("文件名 " + fileName);
    //
    //         // 创建目录并赋予777权限
    //         String directory = "/path";
    //         ftpClient.makeDirectory(directory);
    //         ftpClient.sendCommand("SITE CHMOD 777 " + directory);
    //
    //         // 切换到指定目录
    //         ftpClient.changeWorkingDirectory(directory);
    //
    //         // 设置缓冲区,加快上传速度
    //         ftpClient.setBufferSize(1024 * 1024 * 10);
    //
    //         // 上传文件到FTP服务器
    //         ftpClient.storeFile(fileName, inputStream);
    //
    //         // 关闭FTP连接和输入流
    //         inputStream.close();
    //         ftpClient.logout();
    //         ftpClient.disconnect();
    //
    //         log.info("上传成功");
    //         return R.success(fileName + "上传成功");
    //
    //     } catch (IOException e) {
    //         e.printStackTrace();
    //
    //         log.info("上传失败");
    //         return R.success("上传失败");
    //     }
    // }
}




